#ifndef __WIZ_DATATYPE_SHAREDPTR_HPP__SHANHAOBO_19800429__
#define __WIZ_DATATYPE_SHAREDPTR_HPP__SHANHAOBO_19800429__

#include "../../../Base/WizBase.hpp"
#include "./WizDataTypeSharedCounter.hpp"

/// Forward Declare
namespace Wiz
{
    //////////////////////////////////////////////////////////////////////////
    /// WeakPtr
    namespace WeakPtr
    {
        template<class ElementT> class Type;
    } /// end of namespace WeakPtr
    template<class ElementT> class Enable_SharedFromThis;
    //////////////////////////////////////////////////////////////////////////
} /// end of namespace Wiz

namespace Wiz
{
    //////////////////////////////////////////////////////////////////////////
    template<class T, class Y>
    WIZ_INLINE ::Wiz::Void::Type SPEnable_SharedFromThis
    (
        ::Wiz::SharedCounter::In                    SharedCounterRef,
        ::Wiz::Enable_SharedFromThis<T> const *     PE,
        Y const *                                   PPtr
    )
    {
        if (::Wiz::IsValidPtr(PE))
        {
            PE->m_WeakPtr.Set(::Wiz::Cast::Const<Y*>(PPtr), SharedCounterRef);
        }
    }

    WIZ_INLINE ::Wiz::Void::Type SPEnable_SharedFromThis
    (
        ::Wiz::SharedCounter::In,
        ...
    )
    {
    }
    //////////////////////////////////////////////////////////////////////////

    namespace SharedPtr
    {
        template<class ElementT>
        class Type : public ::Wiz::UnspecifiedBool::Type<Type<ElementT>>
        {
        private:
            typedef Type<ElementT>                                              tThis;
            typedef ElementT                                                    tValue;
            typedef ElementT*                                                   tElementPtr;
            typedef typename ::Wiz::TypeTraits::Get::Reference<ElementT>::tType tReference;
        public:
            ////////////////////////////////////////////////////////////////////////
            /// For ::Wiz::UnspecifiedBool::Type
            ::Wiz::Bool::Type IsTrue() const
            {
                return this->IsValid();
            }
            ////////////////////////////////////////////////////////////////////////
            WIZ_INLINE ::Wiz::Bool::Type NotValid() const
            {
                return ::Wiz::NotValidPtr(this->m_ManagedPtr);
            }
            WIZ_INLINE ::Wiz::Bool::Type IsValid() const
            {
                return this->NotValid() == ::Wiz::Bool::False;
            }
            WIZ_INLINE ::Wiz::Bool::Type operator!() const
            {
                return this->NotValid();
            }
        public:
            Type() : m_ManagedPtr(WIZ_NULLPTR), m_SharedCounter()
            {
            }
            template<class Y>
            explicit Type(Y* p) : m_ManagedPtr(p), m_SharedCounter(p)
            {
                ::Wiz::SPEnable_SharedFromThis(m_SharedCounter, p, p);
            }
            template<class Y, class D>
            Type(Y* p, D d) : m_ManagedPtr(p), m_SharedCounter(p, d)
            {
                ::Wiz::SPEnable_SharedFromThis(m_SharedCounter, p, p);
            }
            template<class Y, class D, class A>
            Type(Y* p, D d, A a) : m_ManagedPtr(p), m_SharedCounter(p, d, a)
            {
                ::Wiz::SPEnable_SharedFromThis(m_SharedCounter, p, p);
            }
            Type& operator=(Type const & r)
            {
                m_ManagedPtr = r.m_ManagedPtr;
                m_SharedCounter = r.m_SharedCounter;
                return *this;
            }
            template<class Y>
            explicit Type(::Wiz::WeakPtr::Type<Y> const & r)
                : m_SharedCounter(r.m_SharedCounter)
            {
                m_ManagedPtr = r.m_ManagedPtr;
            }
            template<class Y>
            Type(Type<Y> const & r)
                : m_ManagedPtr(r.m_ManagedPtr)
                , m_SharedCounter(r.m_SharedCounter)
            {
            }
            template<class Y>
            Type(Type<Y> const & r, ElementT* p)
                : m_ManagedPtr(p)
                , m_SharedCounter(r.m_SharedCounter)
            {
            }
            template<class Y>
            Type(Type<Y> const & r, ::Wiz::Cast::Tag::Static)
                : m_ManagedPtr(::Wiz::Cast::Static<ElementT *>(r.m_ManagedPtr))
                , m_SharedCounter(r.m_SharedCounter)
            {
            }
            template<class Y>
            Type(Type<Y> const & r, ::Wiz::Cast::Tag::Const)
                : m_ManagedPtr(::Wiz::Cast::Const<ElementT *>(r.m_ManagedPtr))
                , m_SharedCounter(r.m_SharedCounter)
            {
            }
            template<class Y>
            Type(Type<Y> const & r, ::Wiz::Cast::Tag::Dynamic)
                : m_ManagedPtr(::Wiz::Cast::Dynamic<ElementT *>(r.m_ManagedPtr))
                , m_SharedCounter(r.m_SharedCounter)
            {
                if (::Wiz::NotValidPtr(m_ManagedPtr))
                {
                    m_SharedCounter = ::Wiz::SharedCounter::Type();
                }
            }
            template<class Y>
            Type(Type<Y> const & r, ::Wiz::Cast::Tag::Polymorphic)
                : m_ManagedPtr(::Wiz::Cast::Dynamic<ElementT *>(r.m_ManagedPtr))
                , m_SharedCounter(r.m_SharedCounter)
            {
                if (::Wiz::NotValidPtr(m_ManagedPtr))
                {
                    Wiz::Exception::Throw(::std::bad_cast());
                }
            }

            //////////////////////////////////////////////////////////////////////////
            template<class Y>
            Type& operator=(Type<Y> const & r)
            {
                m_ManagedPtr = r.m_ManagedPtr;
                m_SharedCounter = r.m_SharedCounter;
                return *this;
            }
            ////////////////////////////////////////////////////////////////////////
            ::Wiz::Void::Type Reset()
            {
                tThis().Swap(*this);
            }
            template<class Y>
            ::Wiz::Void::Type Reset(Y * p)
            {
                WIZ_ASSERT(::Wiz::NotValidPtr(p) || (p != m_ManagedPtr));
                tThis(p).Swap(*this);
            }
            template<class Y, class D>
            ::Wiz::Void::Type Reset(Y * p, D d)
            {
                tThis(p, d).Swap(*this);
            }
            template<class Y, class D, class A>
            ::Wiz::Void::Type Reset(Y * p, D d, A a)
            {
                tThis(p, d, a).Swap(*this);
            }
            template<class Y>
            ::Wiz::Void::Type Reset(Type<Y> const & r, ElementT * p)
            {
                tThis(r, p).Swap(*this);
            }
            ////////////////////////////////////////////////////////////////////////
            tReference operator*() const // never throws
            {
                WIZ_ASSERT(::Wiz::IsValidPtr(m_ManagedPtr));
                return *m_ManagedPtr;
            }
            tElementPtr operator->() const // never throws
            {
                WIZ_ASSERT(::Wiz::IsValidPtr(m_ManagedPtr));
                return m_ManagedPtr;
            }
            tElementPtr Get() const // never throws
            {
                return m_ManagedPtr;
            }
            ::Wiz::Bool::Type Unique() const
            {
                return m_SharedCounter.Unique();
            }
            ::Wiz::Int::Type SharedCount() const
            {
                return m_SharedCounter.SharedCount();
            }
            ::Wiz::Void::Type Swap(tThis& other)
            {
                ::Wiz::Swap(m_ManagedPtr, other.m_ManagedPtr);
                m_SharedCounter.Swap(other.m_SharedCounter);
            }
            template<class Y>
            ::Wiz::Bool::Type Less(Type<Y> const & rhs) const
            {
                return m_SharedCounter < rhs.m_SharedCounter;
            }
            ::Wiz::Void::Ptr GetDeleter(::std::type_info const & ti) const
            {
                return m_SharedCounter.GetDeleter( ti );
            }
        public:
            template<class Y> friend class ::Wiz::SharedPtr::Type;
            //template<class Y> friend class ::Wiz::WeakPtr::Type;
            tElementPtr                   m_ManagedPtr;
            ::Wiz::SharedCounter::Type      m_SharedCounter;
        };  // Type
    } /// end of namespace SharedPtr

    template<class ElementT, class U>
    WIZ_INLINE ::Wiz::Bool::Type operator==(::Wiz::SharedPtr::Type<ElementT> const & a, ::Wiz::SharedPtr::Type<U> const & b)
    {
        return a.Get() == b.Get();
    }

    template<class ElementT, class U>
    WIZ_INLINE ::Wiz::Bool::Type operator!=(::Wiz::SharedPtr::Type<ElementT> const & a, ::Wiz::SharedPtr::Type<U> const & b)
    {
        return a.Get() != b.Get();
    }

    template<class ElementT, class U>
    WIZ_INLINE ::Wiz::Bool::Type operator<(::Wiz::SharedPtr::Type<ElementT> const & a, ::Wiz::SharedPtr::Type<U> const & b)
    {
        return a.Less(b);
    }

    template<class ElementT>
    WIZ_INLINE ::Wiz::Void::Type Swap(::Wiz::SharedPtr::Type<ElementT> & a, ::Wiz::SharedPtr::Type<ElementT> & b)
    {
        a.Swap(b);
    }

    template<class ElementT, class U>
    ::Wiz::SharedPtr::Type<ElementT> StaticPointerCast(::Wiz::SharedPtr::Type<U> const & r)
    {
        return ::Wiz::SharedPtr::Type<ElementT>(r, ::Wiz::Cast::Tag::Static());
    }

    template<class ElementT, class U>
    ::Wiz::SharedPtr::Type<ElementT> ConstPointerCast(::Wiz::SharedPtr::Type<U> const & r)
    {
        return ::Wiz::SharedPtr::Type<ElementT>(r, ::Wiz::Cast::Tag::Const());
    }

    template<class ElementT, class U>
    ::Wiz::SharedPtr::Type<ElementT> DynamicPointerCast(::Wiz::SharedPtr::Type<U> const & r)
    {
        return ::Wiz::SharedPtr::Type<ElementT>(r, ::Wiz::Cast::Tag::Dynamic());
    }

    // shared_*_cast names are deprecated. Use *_pointer_cast instead.

    template<class ElementT, class U>
    ::Wiz::SharedPtr::Type<ElementT> SharedStaticCast(::Wiz::SharedPtr::Type<U> const & r)
    {
        return ::Wiz::SharedPtr::Type<ElementT>(r, ::Wiz::Cast::Tag::Static());
    }

    template<class ElementT, class U>
    ::Wiz::SharedPtr::Type<ElementT> SharedDynamicCast(::Wiz::SharedPtr::Type<U> const & r)
    {
        return ::Wiz::SharedPtr::Type<ElementT>(r, ::Wiz::Cast::Tag::Dynamic());
    }

    template<class ElementT, class U>
    ::Wiz::SharedPtr::Type<ElementT> SharedPolymorphicCast(::Wiz::SharedPtr::Type<U> const & r)
    {
        return ::Wiz::SharedPtr::Type<ElementT>(r, ::Wiz::Cast::Tag::Polymorphic());
    }

    template<class ElementT, class U>
    ::Wiz::SharedPtr::Type<ElementT> SharedPolymorphicDowncast(::Wiz::SharedPtr::Type<U> const & r)
    {
        WIZ_ASSERT(::Wiz::Cast::Dynamic<ElementT *>(r.Get()) == r.Get());
        return SharedStaticCast<ElementT>(r);
    }

    template<class ElementT>
    WIZ_INLINE ElementT* GetPointer(::Wiz::SharedPtr::Type<ElementT> const & p)
    {
        return p.Get();
    }

    template<class D, class ElementT>
    WIZ_INLINE D* GetDeleter(::Wiz::SharedPtr::Type<ElementT> const & p)
    {
        return ::Wiz::Cast::Static<D *>(p.GetDeleter(::std::typeid(D)));
    }
    //////////////////////////////////////////////////////////////////////////
} /// end of namespace Wiz


////////////////////////////////////////////////////////////////////////////////

#   define WIZ_DECLARE_SHAREDPTR(t)                     \
    typedef ::Wiz::SharedPtr::Type<t>         SharedPtr;      \
    typedef const ::Wiz::SharedPtr::Type<t>&  InSharedPtr;    \
    typedef ::Wiz::SharedPtr::Type<t>&        OutSharedPtr;   \
    typedef ::Wiz::SharedPtr::Type<t>&        InOutSharedPtr;

////////////////////////////////////////////////////////////////////////////////

#endif /*__WIZ_DATATYPE_SHAREDPTR_HPP__SHANHAOBO_19800429__*/
